package br.com.dyad.commons.data;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import br.com.dyad.commons.reflect.ReflectUtil;

public class HashMapIndexList {

	private String fieldNames;
	@SuppressWarnings("unchecked")
	//é um ponteiro pra lista da datalist que contem o indice!
	private List<AppEntity> arrayList;
	
	private HashMap<String, HashMap<Long,AppEntity>> map;
	
	@SuppressWarnings("unchecked")
	public HashMapIndexList( List aArrayList, String strFieldNames ){
		this.arrayList = aArrayList;
		this.fieldNames = strFieldNames;
		this.map = new HashMap();
	}

	@SuppressWarnings("unchecked")
	public void build() throws Exception{		
		this.map.clear();
		
		for (int i = 0; i < arrayList.size(); i++) {
			AppEntity ob = arrayList.get(i);
			
			HashMap<Long,AppEntity> result = this.getListToInsertObject( ob );
			String key = this.getKeyFromObject(ob);

			result.put(ob.getId(), ob);
			
			this.map.put(key, result);			
		}	
		System.out.println( "built " );
	}

	@SuppressWarnings("unchecked")
	private HashMap<Long,AppEntity> getListToInsertObject(Object ob) throws Exception {
		String key = this.getKeyFromObject(ob);
		if ( this.map.get(key) != null ){
			return (HashMap<Long,AppEntity>)this.map.get(key);
		} else {
			return new HashMap<Long,AppEntity>();
		}
	}

	public String getKeyFromObject(Object ob) throws Exception {
		String[] arFieldNames = this.fieldNames.split(";");
		String[] arFieldValues = new String[arFieldNames.length];
		for (int i = 0; i < arFieldNames.length; i++) {
			//agora pode pesquisar por uma propriedade de uma propriedade do objeto
			Object value = getFieldValue(ob, arFieldNames[i]);//ReflectUtil.getFieldValueIgnoreCase(ob, arFieldNames[i]);			
			if ( value == null ){
				arFieldValues[i] = "";
			} else {
				if ( value instanceof AppEntity ){
					Long id = ((AppEntity)value).getId();
					if ( id == null ){
						arFieldValues[i] = "";
					} else {
						arFieldValues[i] = StringUtils.chomp(id + "");
					}
				} else {
					arFieldValues[i] = StringUtils.chomp(value + ""); 						
				}
			}

		}
		return StringUtils.join(arFieldValues,";");
	}
	
	/**
	 * Pesquisa por uma propriedade dentro da outra caso a string esteja separada por pontos
	 * 
	 * @param object
	 * @param fieldName
	 * @return
	 */
	protected Object getFieldValue(Object object, String fieldName) {
		final String FIELD_SEPARATOR = ".";
		String props[] = StringUtils.split(fieldName, FIELD_SEPARATOR);
		
		if (props != null && props.length > 1) {			
			for (String temp : props) {
				if (object == null) {
					return null;
				}
				object = ReflectUtil.getFieldValueIgnoreCase(object, temp);
			}
			
			return object;
		}
		
		return ReflectUtil.getFieldValueIgnoreCase(object, fieldName);
	}
	
	public String getOldKeyFromObject(Object ob) {
		if (!(ob instanceof AppEntity)){
			throw new RuntimeException("oldValue not implemented on " + ob.getClass());			
		}
		String[] arFieldNames = this.fieldNames.split(";");
		String[] arFieldValues = new String[arFieldNames.length];
		for (int i = 0; i < arFieldNames.length; i++) {
			try {				
				Object value = ((AppEntity)ob).getOldValue(arFieldNames[i]);
				String strValue = value != null ? value + "" : "";
				strValue = StringUtils.chomp(strValue);
				arFieldValues[i] = strValue;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return StringUtils.join(arFieldValues,";");
	}
	
	public Boolean find(Object... keys){
		HashMap<Long,AppEntity> map = this.get(keys);
		return map != null && ! map.isEmpty();
	}	
	
	public Collection<AppEntity>  findObjectByKey( Object... keys ){
		HashMap<Long,AppEntity> map = this.get(keys);
		return map != null && ! map.isEmpty() ? map.values() : null;
	}

	public HashMap<Long,AppEntity> get(Object... keys){
		if ( keys == null ){
			String[] keysString = new String[1];
			keysString[0] = "";
			return this.getByStringKeys(keysString);
		}
		String[] keysString = new String[keys.length]; 
		for (int i = 0; i < keys.length; i++) {
			if ( keys[i] == null ){
				keysString[i] = "";
			} else {
				if ( keys[i] instanceof AppEntity ){
					Long id = ((AppEntity)keys[i]).getId();
					if ( id == null ){
						keysString[i] = "";
					} else {
						keysString[i] = StringUtils.chomp(id + "");
					}
				} else {
					keysString[i] = StringUtils.chomp(keys[i] + "");
				}
			}
		}		
		return this.getByStringKeys(keysString);
	}

	private HashMap<Long,AppEntity> getByStringKeys(String[] keys){
		String key = StringUtils.join(keys,";");
		HashMap<Long,AppEntity> ret = this.map.get(key); 
		return ret;
	}	

	public HashMap<String, HashMap<Long,AppEntity>> getMap() {
		return map;
	}

	@SuppressWarnings("unchecked")
	public void reIndexObject(AppEntity object) throws Exception {								

		// Insert the new index value
		String key = this.getKeyFromObject(object);
		
		// Delete the pointer to object in this index.
		String oldKey = this.getOldKeyFromObject(object);
		if ( ! oldKey.equals(key) && this.map.get(oldKey) != null){
			HashMap<Long,AppEntity> map = this.map.get(oldKey);
			map.remove(object);
			if ( map.isEmpty() ){
				this.map.remove(oldKey);
			}
		}
	
		this.insertObject(object);
	}

	public void deleteObject(AppEntity object) throws Exception {
		String key = this.getKeyFromObject(object);
		HashMap<Long,AppEntity> map = this.map.get(key);
		if ( map != null){
			map.remove(object.getId());
			if ( map.isEmpty() ){
				this.map.remove(key);
			}
		}				
		System.out.println("map -> " + key );
	}

	public void insertObject(AppEntity object) throws Exception {
		// Insert the new index value
		String key = this.getKeyFromObject(object);

		HashMap<Long,AppEntity> arrayList = this.map.get(key);
		if ( arrayList != null ){
			if ( ! arrayList.containsKey( object.getId() ) ){
				arrayList.put(object.getId(), object );
			}
		} else {
			arrayList = new HashMap<Long,AppEntity>();
			arrayList.put(object.getId(), object);
			this.map.put(key, arrayList);
		}				
	}	
}
